package com.loklok.code.confuse.service;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiType;
import com.intellij.psi.search.GlobalSearchScope;
import com.loklok.code.confuse.extend.VmFileType;
import com.loklok.code.confuse.extension.FeignInterfaceInfoCache;
import com.loklok.code.confuse.model.ClassMetaData;
import com.loklok.code.confuse.model.CodePathConfig;
import com.loklok.code.confuse.model.FeignInterfaceInfo;
import com.loklok.code.confuse.util.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class FullUpdateTemplateService {

    public static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(
            Math.min(4, Runtime.getRuntime().availableProcessors()));

    /**
     *  全量更新模板
     * @param
     */
    public static void updateTemplate(AnActionEvent anActionEvent, CodePathConfig config) {
        Project project = anActionEvent.getProject();

        if (DumbService.isDumb(project)) {
            DumbService.getInstance(project).showDumbModeNotification("请等待索引加载完成后再执行操作");
            return;
        }

        List<PsiClass> classes = FeignInterfaceFinderService.getFeignClientPsiClasses(anActionEvent);
        if (CollectionUtils.isEmpty(classes)) {
            return;
        }

        FeignInterfaceInfoCache instance = FeignInterfaceInfoCache.getInstance(project);
        Map<String, FeignInterfaceInfo> feignInterfaceInfoMap = instance.getFeignInterfaceInfoMap();
        if(MapUtils.isEmpty(feignInterfaceInfoMap)){
            Messages.showErrorDialog("未更新 FeignClient 信息, 请使用插件【update feign info cache】更新", "Error");
            return;
        }
        Map<String, List<FeignInterfaceInfo>> feignClassNameMap = feignInterfaceInfoMap.values().stream().collect(Collectors.groupingBy(FeignInterfaceInfo::getFeignClassName));

        // 遍历 classes 如果 每个 classe 的 getName 存在 feignClassNameMap ，则取出所有的 feignClassNameMap values ，并放在一个 List 中
        List<FeignInterfaceInfo> all = classes.stream().filter(it -> feignClassNameMap.containsKey(it.getQualifiedName())).map(it -> feignClassNameMap.get(it.getQualifiedName())).flatMap(Collection::stream).collect(Collectors.toList());

        if(CollectionUtils.isEmpty(all)){
            Messages.showErrorDialog("未匹配到 FeignClient 信息, 请使用插件【update feign info cache】更新", "Error");
            return;
        }

        // 从本地加载 模板提示词
        LocalFileUtils.PromptTemplate.readAllFilesRecursively(config.getDeepSeekPromptFile());

        List<ClassMetaData> classMetaDataList = getClassMetaData(all, project, true, config.getNotCollectClass());

        int total = classMetaDataList.size();
        AtomicInteger processed = new AtomicInteger(0);

        ProgressManager.getInstance().run(new Task.Backgroundable(project, "生成模板中...", true) {
            @Override
            public void run(@NotNull ProgressIndicator indicator) {
                indicator.setIndeterminate(false);
                indicator.setFraction(0.0);
                indicator.setText("正在生成模板...");

                classMetaDataList.stream()
                        .forEach(item -> {
                            // 在每次循环中检查是否取消
                            indicator.checkCanceled();
                            doGenerateTemplateCodeAsync(anActionEvent, config, item, indicator, processed, total);
                        });

                try {
                    while (processed.get() <  total) {
                        Thread.sleep(1000); // 每隔 1000ms 检查一次
                        indicator.checkCanceled(); // 检查是否取消
                    }
                    // 最终更新一次进度
                    indicator.setFraction(1.0);
                    indicator.setText("模板已全部生成....");
                } catch (ProcessCanceledException e) {
                    ApplicationManager.getApplication().invokeLater(() -> {
                        Messages.showMessageDialog(project, "任务已取消", "取消", Messages.getWarningIcon());
                    });
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    ApplicationManager.getApplication().invokeLater(() ->
                            Messages.showMessageDialog(project, "任务被中断", "错误", Messages.getErrorIcon()));
                }
            }
        });
    }



    public static List<ClassMetaData> getClassMetaData(List<FeignInterfaceInfo> all, Project project, boolean needController, String notCollectClass) {
        List<ClassMetaData> classMetaDataList = new ArrayList<>();
        // all 处理出要生成的 文件数
        all.stream().forEach(item -> {
            buildVoClassMetaData(project, classMetaDataList, item, notCollectClass);
            buildRequestClassMetaData(project, classMetaDataList, item, notCollectClass);
        });

        if (needController) {
            buildControllerClassMetaData(project, classMetaDataList, all);
        }
        return classMetaDataList;
    }

    public static void doGenerateTemplateCodeAsync(AnActionEvent anActionEvent, CodePathConfig config, ClassMetaData item, ProgressIndicator indicator, AtomicInteger processed, int total) {
        Project project = anActionEvent.getProject();
        CompletableFuture.runAsync(() -> {
            String className = item.getClassName();
            String partPckName = item.getPartPckName();
            String templatePath = CommonUtil.pathJoint(config.getCodeTemplate(), partPckName, item.getFeignClientName());
            File targetFile = new File(templatePath, className + ".java.vm");

            if (targetFile.exists()) {
                System.out.println("TemplateCode file exists :" + targetFile.getAbsolutePath());
                int current = processed.incrementAndGet();
                double fraction = (double) current / total;
                ApplicationManager.getApplication().invokeLater(() -> {
                    indicator.setFraction(fraction);
                    indicator.setText("正在生成模板... (" + current + "/" + total + ")");
                });
                return;
            }

            ApplicationManager.getApplication().runReadAction(() -> {
                PsiClass psiClass = JavaPsiFacade.getInstance(project)
                        .findClass(item.getFullClassName(), GlobalSearchScope.allScope(project));

                String classContent = ClassContentLoader.getClassText(psiClass);
                if (StringUtils.isEmpty(classContent)) {
                    return;
                }

                String prompt = getPrompt(item, classContent);


                AsyncOpenAIUtil.getVmBodyAsync(config.getDeepSeekApiKey(), prompt, new AsyncOpenAIUtil.ResultCallback<String>() {
                    @Override
                    public void onSuccess(String vmBody) {
                        if (StringUtils.isEmpty(vmBody)) {
                            ApplicationManager.getApplication().invokeLater(() ->
                                    Messages.showMessageDialog("大模型生成模板为空", "Error", Messages.getErrorIcon()));
                            int current = processed.incrementAndGet();
                            double fraction = (double) current / total;
                            ApplicationManager.getApplication().invokeLater(() -> {
                                indicator.setFraction(fraction);
                                indicator.setText("正在生成模板... (" + current + "/" + total + ")");
                            });
                            return;
                        }

                        // 实时写入文件
                        ApplicationManager.getApplication().invokeLater(() -> {
                            LocalFileUtils.writeFile(anActionEvent, templatePath, className +  ".java.vm", vmBody, VmFileType.INSTANCE);

                            // 更新进度条
                            int current = processed.incrementAndGet();
                            double fraction = (double) current / total;
                            indicator.setFraction(fraction);
                            indicator.setText("正在生成模板... (" + current + "/" + total + ")");
                        });
                    }

                    @Override
                    public void onFailure(Exception e) {
                        ApplicationManager.getApplication().invokeLater(() -> {
                            Messages.showMessageDialog("请求失败：" + e.getMessage(), "Error", Messages.getErrorIcon());

                            int current = processed.incrementAndGet();
                            double fraction = (double) current / total;
                            indicator.setFraction(fraction);
                            indicator.setText("正在生成模板... (" + current + "/" + total + ")");
                        });
                    }
                });
            });
        }, EXECUTOR);
    }

    private static String getPrompt(ClassMetaData item, String classContent) {
        String prompt;
        if(item.voType() || item.requestType()){
            prompt = LocalFileUtils.PromptTemplate.getFileContent("originallyParamVm.txt");
            if(StringUtils.isNotEmpty( prompt)){
                prompt = String.format(prompt, classContent);
            }
        } else {
            prompt = LocalFileUtils.PromptTemplate.getFileContent("originallyControllerVm.txt");
            if(StringUtils.isNotEmpty( prompt)){
                prompt = String.format(prompt, classContent);
            }
        }
        if(StringUtils.isEmpty( prompt)){
            prompt = item.voType() || item.requestType()
                    ? PromptTemplate.getOriginallyParamVmPromptTemplate(item.voType(), classContent)
                    : PromptTemplate.getOriginallyControllerVmPromptTemplate(classContent);
        }
        return prompt;
    }

    private static void buildRequestClassMetaData(Project project, List<ClassMetaData> classMetaDataList, FeignInterfaceInfo item, String notCollectClass) {
        if(StringUtils.isEmpty(item.getParameterType()) || GenericTypeResolver.isWrapperOrVoid(item.getParameterType())){
            return;
        }
        fillClassMetaDataList(project, classMetaDataList, item, item.getParameterType(), ClassMetaDataTypeEnum.REQUEST, notCollectClass);
    }

    private static void buildVoClassMetaData(Project project, List<ClassMetaData> classMetaDataList, FeignInterfaceInfo item, String notCollectClass) {
        if(CollectionUtils.isEmpty(item.getReturnGenericTypes())){
            return;
        }

        String returnType = item.getReturnGenericTypes().get(item.getReturnGenericTypes().size() - 1);
        if(GenericTypeResolver.isWrapperOrVoid(returnType)){
            return;
        }

        fillClassMetaDataList(project, classMetaDataList, item, returnType, ClassMetaDataTypeEnum.VO, notCollectClass);
    }

    private static void fillClassMetaDataList(Project project, List<ClassMetaData> classMetaDataList, FeignInterfaceInfo item,
                                              String paramType, ClassMetaDataTypeEnum typeEnum, String notCollectClass) {
        PsiClass returnClass = JavaPsiFacade.getInstance(project)
                .findClass(paramType, GlobalSearchScope.allScope(project));

        // ironman-service-account  取出是  account
        String feignClientName = item.getFeignAnnotationName().replace("ironman-service-", "")
                .replace("ironman-", "").replace("-", "");

        classMetaDataList.add(new ClassMetaData(paramType, returnClass.getName(), typeEnum, feignClientName));

        PsiType type = JavaPsiFacade.getElementFactory(project).createTypeByFQClassName(paramType);

        if (type instanceof PsiClassType) {
            PsiClass returnTypeClass = ((PsiClassType) type).resolve();
            if (returnTypeClass != null) {

                String basePackageName = returnTypeClass.getContainingClass() != null
                        ? returnTypeClass.getContainingClass().getQualifiedName()
                        : returnTypeClass.getQualifiedName();

                if (basePackageName == null) {
                    return;
                }

                String packagePrefix = basePackageName.substring(0, basePackageName.indexOf("."));

                Set<PsiClass> allClasses = NestedClassCollector.collectAllReferencedClasses(returnTypeClass, new HashSet<>(), packagePrefix);

                // 过滤掉内部类
                allClasses = allClasses.stream()
                        .filter(cls -> cls.getContainingClass() == null)
                        .filter(cls -> {
                            // 过滤掉不需要处理的类
                           return !notCollectClass.contains(cls.getQualifiedName());
                        }).collect(Collectors.toSet());

                for (PsiClass cls : allClasses) {

                    String clsQualifiedName = cls.getQualifiedName();
                    if (clsQualifiedName == null || clsQualifiedName.equals(basePackageName)) {
                        continue; // 排除自己
                    }

                    if (!clsQualifiedName.startsWith(packagePrefix)) {
                        continue; // 只保留本类所在包及子包下的类
                    }

                    classMetaDataList.add(new ClassMetaData(cls.getQualifiedName(), cls.getName(), typeEnum, feignClientName));
                }
            }
        }
    }

    private static void buildControllerClassMetaData(Project project, List<ClassMetaData> classMetaDataList, List<FeignInterfaceInfo> all) {

        Map<String, List<FeignInterfaceInfo>> map = all.stream().collect(Collectors.groupingBy(FeignInterfaceInfo::getFeignClassName));

        Set<String> feignClassNameSet = all.stream().map(it -> it.getFeignClassName() + "_" + it.getFeignAnnotationName()).collect(Collectors.toSet());

        feignClassNameSet.stream().forEach(item -> {

            String feignClassName = item.split("_")[0];
            String feignAnnotationName = item.split("_")[1];

            PsiClass returnClass = JavaPsiFacade.getInstance(project)
                    .findClass(feignClassName, GlobalSearchScope.allScope(project));

            String feignClientName = feignAnnotationName.replace("ironman-service-", "")
                    .replace("ironman-", "").replace("-", "");

            String className = returnClass.getName().replace("Feign", "Controller");
            ClassMetaData classMetaData = new ClassMetaData(feignClassName, className, ClassMetaDataTypeEnum.CONTROLLER, feignClientName);
            List<FeignInterfaceInfo> feignInterfaceInfos = map.get(feignClassName);
            if(CollectionUtils.isNotEmpty(feignInterfaceInfos)){
                Set<String> collect = feignInterfaceInfos.stream().map(FeignInterfaceInfo::getMethodName).collect(Collectors.toSet());
                classMetaData.setMethodNames(collect);
            }
            classMetaDataList.add(classMetaData);
        });
    }
}
